using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using Microsoft.DirectX.Direct3D;

namespace Emlyn.Applications.Scratch.Graphics.Tools
{
    public class ScrollingBackground: IDisposable
    {
        DirectXWrapper _wrapper = null;
        Bitmap _tilesBitmap = null;
        Surface _tileBuffer = null;
        Surface _tiles = null;
        MapData _mapData = null;
        int _tileWidth = 10;
        int _tileHeight = 10;

        public ScrollingBackground(
            DirectXWrapper aWrapper, 
            Bitmap aTilesBitmap, 
            int aTileWidth, 
            int aTileHeight,
            MapData aMapData
            )
        {
            if (aWrapper == null)
                throw new ArgumentNullException("aWrapper");
            if (aTilesBitmap == null)
                throw new ArgumentNullException("aTilesBitmap");
            if (aMapData == null)
                throw new ArgumentNullException("aMapData");

            _wrapper = aWrapper;
            _tilesBitmap = aTilesBitmap;
            _tileWidth = aTileWidth;
            _tileHeight = aTileHeight;
            _mapData = aMapData;

            DoInit();
        }

        public void ReInit()
        {
            DoDispose();
            DoInit();
        }

        private void DoInit()
        {
            _tileBuffer = _wrapper.CreateImageSurface(
                _wrapper._backbuffer.Description.Width + _tileWidth * 2,
                _wrapper._backbuffer.Description.Height + _tileHeight * 2
                );

            _tiles = _wrapper.LoadSurfaceFromBitmap(_tilesBitmap);
        }

        public void DoDispose()
        {
            if (_tileBuffer != null && !_tileBuffer.Disposed)
                _tileBuffer.Dispose();
            if (_tiles != null && !_tiles.Disposed)
                _tiles.Dispose();
        }

        public int TileWidth
        {
            get { return _tileWidth; }
        }

        public int TileHeight
        {
            get { return _tileHeight; }
        }

        public void GetCurrentTopLeftTile(int aScrollX, int aScrollY, out int x, out int y)
        {
            x = aScrollX / TileWidth;
            y = aScrollY / TileHeight;
        }

        public int NumTilesWide
        {
            get { return _mapData.Width; }
        }

        public int NumTilesHigh
        {
            get { return _mapData.Height; }
        }


        public void GetBackBufferTileDimensions(out int width, out int height)
        {
            width = Convert.ToInt32(Math.Ceiling((double)(_wrapper._backbuffer.Description.Width / TileWidth))) + 3;
            height = Convert.ToInt32(Math.Ceiling((double)(_wrapper._backbuffer.Description.Height / TileHeight))) + 3;
        }

        public void BuildTileBuffer(int aScrollX, int aScrollY, int aViewPortWidth, int aViewPortHeight)
        {
            int lfirsttileX;
            int lfirsttileY;
            int lwidth; // tile width of the viewport
            int lheight; // tile height of the viewport

            GetCurrentTopLeftTile(aScrollX, aScrollY, out lfirsttileX, out lfirsttileY);
//            GetBackBufferTileDimensions(out lwidth, out lheight);

            lwidth = Convert.ToInt32(Math.Ceiling((double)(aViewPortWidth / TileWidth))) + 3; // add 3 for some extra tiles
            lheight = Convert.ToInt32(Math.Ceiling((double)(aViewPortHeight / TileHeight))) + 3; // add 3 for some extra tiles

            for (int Y = lfirsttileY; Y < Math.Min(lfirsttileY + lheight, _mapData.Height); Y++)
            {
                for (int X = lfirsttileX; X < Math.Min(lfirsttileX + lwidth, _mapData.Width); X++)
                {
                    int lMapItem = _mapData.GetData(X, Y);

                    Rectangle lSourceRect = new Rectangle(
                        ((lMapItem % 20) * TileWidth),
                        ((lMapItem / 20) * TileHeight),
                        TileWidth,
                        TileHeight
                        );
                    Rectangle lTargetRect = new Rectangle(
                        (X - lfirsttileX) * TileWidth,
                        (Y - lfirsttileY) * TileHeight,
                        TileWidth,
                        TileHeight
                        );

                    _wrapper.DrawOntoDestinationSurface(
                        _tiles, lSourceRect, _tileBuffer, lTargetRect
                    );
                }
            }
        }

        public void WriteBackgroundOntoSurface(Surface aSurface, int aScrollX, int aScrollY, int aViewPortWidth, int aViewPortHeight)
        {
            Rectangle lLoadedSurfaceRect =
                new Rectangle(
                    aScrollX % TileWidth,
                    aScrollY % TileHeight,
                    aViewPortWidth,
                    aViewPortHeight
                    );

            _wrapper.DrawOntoDestinationSurface(_tileBuffer, lLoadedSurfaceRect, aSurface, new Point(0, 0));
        }

        public void WriteBackgroundOntoBackBuffer(int aScrollX, int aScrollY)
        {
            Rectangle lLoadedSurfaceRect =
                new Rectangle(
                    aScrollX % TileWidth,
                    aScrollY % TileHeight,
                    _wrapper._backbuffer.Description.Width,
                    _wrapper._backbuffer.Description.Height
                    );

            _wrapper.DrawOntoDestinationSurface(_tileBuffer, lLoadedSurfaceRect, _wrapper._backbuffer, new Point(0, 0));
        }

        #region IDisposable Members

        public void Dispose()
        {
            DoDispose();
        }

        #endregion
    }
}
